//
//  YTBleManager.m
//  YUNT
//
//  Created by 梁炜东 on 2017/11/19.
//  Copyright © 2017年 梁炜东. All rights reserved.
//

#import "YTBleManager.h"

static NSString *const ServiceUUID1 =  @"FFE5";
static NSString *const notiyCharacteristicUUID =  @"FFE4";
static NSString *const readwriteCharacteristicUUID =  @"FFE9";


@interface YTBleManager()<CBCentralManagerDelegate, CBPeripheralDelegate>
@property(nonatomic, strong)CBCentralManager *cMgr;
@property(nonatomic, strong)CBPeripheral *peripheral;
@property(nonatomic, copy)BleManagerBlock block;
@property(nonatomic, strong)NSMutableArray *devices;
@property(nonatomic, strong)UIAlertController *alertVC;

/**
 写数据特征
 */
@property (nonatomic, strong) CBCharacteristic *writeCharacteristic;
/**
 监听下行数据特征
 */
@property (nonatomic, strong) CBCharacteristic *notifyCharacteristic;

/**
 Read  Write Without Response Notify
 */
@property (nonatomic, strong) CBCharacteristic *flowCharacteristic;
@end

@implementation YTBleManager
-(void)reloadBleListBlock:(BleManagerBlock)block
{
    self.block = block;
}
+ (YTBleManager *)shareModel
{
    static YTBleManager *shareManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        shareManager = [[self alloc] init];
    });
    return shareManager;
    
}
-(instancetype)init
{
    self = [super init];
    if (self)
    {
        _devices = [NSMutableArray array];
        _cMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{CBCentralManagerOptionShowPowerAlertKey:@(YES)}];

    }
    return self;
}
//只要中心管理者初始化 就会触发此代理方法 判断手机蓝牙状态
-(void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    switch (central.state)
    {
        case 0:
        {
            NSLog(@"CBCentralManagerStateUnknown");
        }
            break;
        case 1:
        {
             NSLog(@"CBCentralManagerStateResetting");
        }
            break;
        case 2:
        {
            NSLog(@"CBCentralManagerStateUnsupported");//不支持蓝牙
        }
            break;
        case 3:
        {
            NSLog(@"CBCentralManagerStateUnauthorized");
        }
            break;
        case 4:
        {
            NSLog(@"CBCentralManagerStatePoweredOff");//蓝牙未开启
            //弹出alert 让他开蓝牙
            _alertVC = [UIAlertController alertControllerWithTitle:Language(@"蓝牙") message:Language(@"请蓝牙“允许新的连接”") preferredStyle:UIAlertControllerStyleAlert];
            [_alertVC addAction:[UIAlertAction actionWithTitle:Language(@"设置") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                [YTTools gotoBluetooth];
            }]];
            [_alertVC addAction:[UIAlertAction actionWithTitle:Language(@"取消") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {}]];
            [kRootController presentViewController:_alertVC animated:YES completion:nil];
        }
            break;
        case 5:
        {
            if (_alertVC)
            {
                [_alertVC dismissViewControllerAnimated:YES completion:nil];
                _alertVC = nil;
            }
            NSLog(@"CBCentralManagerStatePoweredOn");//蓝牙已开启
            // 在中心管理者成功开启后再进行一些操作
            // 搜索外设
            //services如果你传一个值的话，就是指搜索这个services的蓝牙
            [self.cMgr scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@(NO)}];
        }
            break;
        default:
            break;
    }
}
//搜索外围设备
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
{/*central 中心管理者
  peripheral // 外设
  advertisementData // 外设携带的数据
  RSSI // 外设发出的蓝牙信号强度
  */
    if (self.devices.count <= 0)
    {
        [self.devices addObject:peripheral];
    }else
    {
//      NSGenericException这个异常最容易出现在foreach操作中，在for in循环中如果修改所遍历的数组，无论你是add或remove，都会出错
        BOOL exist = NO;
        for (CBPeripheral *per in self.devices)
        {
            if ([per.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString])
            {
//                [self.devices addObject:peripheral];
                exist = YES;
                break;
            }
        }
        if (!exist)
        {
            [self.devices addObject:peripheral];
        }
    }
    self.block(self.devices);
}


-(void)connectPeripheral:(CBPeripheral *)peripheral
{
    if ([peripheral.name hasPrefix:@"梁炜东的MacBook Pro"])
    {
        // 标记我们的外设,让他的生命周期 = vc
        self.peripheral = peripheral;
        // 发现完之后就是进行连接
        [self.cMgr connectPeripheral:peripheral options:nil];
        //去连接之后 就停止扫描
        [self.cMgr stopScan];
    }
}

//中心管理者连接外设成功
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    /**
     central 中心管理者
     peripheral // 外设
     */
    // 连接成功之后,可以进行服务和特征的发现
    //  设置外设的代理
    self.peripheral.delegate = self;
    // 外设发现服务,传nil代表不过滤
    // 这里会触发外设的代理方法 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
    [self.peripheral discoverServices:nil];
    [SVProgressHUD showInfoWithStatus:[NSString stringWithFormat:@"设备：%@--连接成功",peripheral.name]];
}
#pragma mark CBPeripheralDelegate
// 外设连接失败
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"外设连接失败了");
    [SVProgressHUD showInfoWithStatus:[NSString stringWithFormat:@"设备：%@--连接失败",peripheral.name]];
}
// 丢失连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"%s, line = %d, %@=断开连接", __FUNCTION__, __LINE__, peripheral.name);
}
//获取服务后的回调
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    if (error)
    {
        NSLog(@"didDiscoverServices : %@", [error localizedDescription]);
        return;;
    }
    for (CBService *s in peripheral.services)
    {
        NSLog(@"Service found with UUID : %@", s.UUID);
        [s.peripheral discoverCharacteristics:nil forService:s];
    }
}
//获取特征后的回调
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    if (error)
    {
        NSLog(@"didDiscoverCharacteristicsForService error : %@", [error localizedDescription]);
        return;
    }
    
    for (CBCharacteristic *c in service.characteristics)
    {
        NSLog(@"\n>>>\t特征UUID FOUND(in 服务UUID:%@): %@ (data:%@)",service.UUID.description,c.UUID,c.UUID.data);
        /*
         根据特征不同属性去读取或者写
         if (c.properties==CBCharacteristicPropertyRead) {
         }
         if (c.properties==CBCharacteristicPropertyWrite) {
         }
         if (c.properties==CBCharacteristicPropertyNotify) {
         }
         */
        if ([c.UUID isEqual:[CBUUID UUIDWithString:@"0783B03e-8535-B5A0-7140-A304D2495CB8"]])
        {//Server TX Data ,Read Notify
            //Subscribing to a Characteristic’s Value 订阅
            [peripheral setNotifyValue:YES forCharacteristic:c];
            // read the characteristic’s value，回调didUpdateValueForCharacteristic
            [peripheral readValueForCharacteristic:c];
            self.notifyCharacteristic = c;
        }else if ([c.UUID isEqual:[CBUUID UUIDWithString:@"0783B03e-8535-B5A0-7140-A304D2495CBA"]])
        { //Server RX Data ,Read  Write Without Response
            self.writeCharacteristic = c;
            
        }else if ([c.UUID isEqual:[CBUUID UUIDWithString:@"0783B03e-8535-B5A0-7140-A304D2495CB9"]])
        { //Flow Control ,Read  Write Without Response Notify
            self.flowCharacteristic = c;
            
        }
    }
}
//订阅的特征值有新的数据时回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    
    if (error) {
        NSLog(@"Error changing notification state: %@",[error localizedDescription]);
    }
    
    [peripheral readValueForCharacteristic:characteristic];
}
//从外围设备读数据
// 更新特征的value的时候会调用 （凡是从蓝牙传过来的数据都要经过这个回调，简单的说这个方法就是你拿数据的唯一方法） 你可以判断是否
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error
{//蓝牙发过来的数据都是走到这里
    if (error)
    {
        NSLog(@"didUpdateValueForCharacteristic error : %@", error.localizedDescription);
        return;
    }
    NSData *data = characteristic.value;
    
    NSLog(@"test:%@", data);
    uint8_t *buf = (uint8_t*)[data bytes];
    NSUInteger bufLen = data.length;
    if (bufLen < 4) {
        NSLog(@"packet error size < 4 %@",data);
        return;
    }
    uint8_t packetHead = buf[0];
    uint8_t len = buf[1];
    //包头
    if (packetHead!=0x0A) {
        NSLog(@"packetHead error %02x %@",packetHead,data);
        return;
    }
    //未收到完整的包
    if (bufLen < len) {
        NSLog(@"packet error size error %@",data);
        return;
    }
    //效验
    uint8_t chechk = 0;
    for (int i=0; i<len-1; i++) {
        chechk+=buf[i];
    }
    NSMutableArray *arrMutable = [[NSMutableArray alloc] init];
    for (int i = 0; i < len; i++) {
        uint8_t unitValue = buf[i];
        NSString *strValue = [NSString stringWithFormat:@"%02x", unitValue];
        [arrMutable addObject:strValue];
    }
//    字符串数组 arrMutable
    if (self.delegate && [self.delegate respondsToSelector:@selector(delegateBleData:)])
    {
        [self.delegate delegateBleData:arrMutable];
    }
}
-(void)sendBleData:(NSData *)data Block:(sendBlock)block
{
    if (_writeCharacteristic)
    {
        [self.peripheral writeValue:data forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
    }
}
#pragma mark 写数据后回调
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error
{
    if (error)
    {
        NSLog(@"Error writing characteristic value: %@",[error localizedDescription]);
        return;
    }
    NSLog(@"写入%@成功", characteristic);
}
//其他：提醒
//有那么多的特征，我们怎么知道哪些特征是用来读数据的，哪些是用来写入的，哪些是需要订阅之后再读的呢？
//如果你们硬件工程师事先告诉你了，或者有完成的开发文档，那么就可以直接知道了，否则你就需要自己去查看特征的属性，推介可以使用下第三方的app——LightBlue，让你更能清楚的看到你蓝牙里面的服务，特征，特征的属性
@end
